package com.tbynet.jwp.framework.interceptor;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.tbynet.jwp.framework.annotation.IgnoreJwpRepositoryRestrict;
import com.tbynet.jwp.framework.annotation.JwpRepositoryRestrict;
import com.tbynet.jwp.framework.core.JwpService;
import com.tbynet.jwp.framework.core.JwpRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 可配置的JwpRepository调用拦截器（注解+包级双控版）
 * 1. 注解：@JwpRepositoryRestrict（强制限制）、@IgnoreJwpRepositoryRestrict（忽略限制）
 * 2. 包级：repositoryRestrictPackages（批量限制）、callerCheckPackages（批量校验调用方）
 */
public class JwpRepositoryCallInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(JwpRepositoryCallInterceptor.class);

    // 可配置项1：允许的调用方父类列表（默认JwpManager）
    private Set<Class<?>> allowedCallerParents;

    // 可配置项2：需要被限制的Repository包范围（批量控制）
    private Set<String> repositoryRestrictPackages;

    // 可配置项3：需要被校验的调用方包范围（批量控制）
    private Set<String> callerCheckPackages;

    // 固定配置：忽略的系统/框架类前缀
    private static final Set<String> IGNORE_CLASS_PREFIXES = new HashSet<>(Arrays.asList(
            "java.", "sun.", "jdk.", "com.jfinal.", "javax.", "org.slf4j.", "org.apache."
    ));

    // ===================== 构造函数 =====================
    public JwpRepositoryCallInterceptor() {
        this.allowedCallerParents = new HashSet<>(Collections.singletonList(JwpService.class));
        this.repositoryRestrictPackages = new HashSet<>();
        this.callerCheckPackages = new HashSet<>();
    }

    public JwpRepositoryCallInterceptor(Set<Class<?>> allowedCallerParents,
                                        Set<String> repositoryRestrictPackages,
                                        Set<String> callerCheckPackages) {
        this.allowedCallerParents = Optional.ofNullable(allowedCallerParents)
                .filter(p -> !p.isEmpty())
                .orElse(new HashSet<>(Collections.singletonList(JwpService.class)));
        this.repositoryRestrictPackages = Optional.ofNullable(repositoryRestrictPackages).orElse(new HashSet<>());
        this.callerCheckPackages = Optional.ofNullable(callerCheckPackages).orElse(new HashSet<>());
    }

    public static JwpRepositoryCallInterceptor create(Set<Class<?>> allowedCallerParents,
                                                      Set<String> repositoryRestrictPackages,
                                                      Set<String> callerCheckPackages) {
        return new JwpRepositoryCallInterceptor(allowedCallerParents, repositoryRestrictPackages, callerCheckPackages);
    }

    // ===================== 核心拦截逻辑（新增注解校验） =====================
    @Override
    public void intercept(Invocation inv) {
        // 1. 获取被拦截的原始Repository类（剥离CGLIB代理）
        Object targetObj = inv.getTarget();
        Class<?> originalTargetClass = getOriginalClass(targetObj.getClass());

        // 2. 判断是否为JwpRepository子类：非目标类直接放行
        if (!JwpRepository.class.isAssignableFrom(originalTargetClass)) {
            inv.invoke();
            return;
        }

        // ====== 新增：注解优先级判断 ======
        // 2.1 若标记@IgnoreJwpRepositoryRestrict：直接放行（优先级最高）
        if (originalTargetClass.isAnnotationPresent(IgnoreJwpRepositoryRestrict.class)) {
            inv.invoke();
            return;
        }

        // 2.2 判断是否需要执行限制：注解强制限制 OR 包级配置限制
        boolean needRestrict = originalTargetClass.isAnnotationPresent(JwpRepositoryRestrict.class)
                || isTargetRepositoryInRestrictPackage(originalTargetClass);

        // 无需限制：直接放行
        if (!needRestrict) {
            inv.invoke();
            return;
        }

        // 3. 校验调用方是否合法
        if (!isCallerValid()) {
            String errorMsg = String.format(
                    "非法调用：禁止非[%s]子类调用受限制的JwpRepository方法！目标类：%s，目标方法：%s",
                    allowedCallerParents.stream().map(Class::getSimpleName).collect(Collectors.joining(",")),
                    originalTargetClass.getName(),
                    inv.getMethod().getName()
            );
            log.error(errorMsg);
            throw new SecurityException(errorMsg);
        }

        // 4. 合法调用：执行目标方法
        inv.invoke();
    }

    // ===================== 核心工具方法（无修改） =====================
    /**
     * 剥离代理类，获取原始目标类（适配CGLIB代理）
     */
    private Class<?> getOriginalClass(Class<?> proxyClass) {
        String className = proxyClass.getName();
        if (className.contains("$$EnhancerByCGLIB$$")) {
            Class<?> superClass = proxyClass.getSuperclass();
            if (superClass != null && superClass != Object.class) {
                return superClass;
            }
        }
        return proxyClass;
    }

    /**
     * 判断被拦截的Repository是否在repositoryRestrictPackages包下
     */
    private boolean isTargetRepositoryInRestrictPackage(Class<?> originalTargetClass) {
        if (repositoryRestrictPackages.isEmpty()) {
            return false;
        }
        String targetClassName = originalTargetClass.getName();
        return repositoryRestrictPackages.stream().anyMatch(targetClassName::startsWith);
    }

    /**
     * 校验调用方是否为允许的父类子类
     */
    private boolean isCallerValid() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return Arrays.stream(stackTrace)
                .filter(this::isValidCallerStack)
                .anyMatch(this::isCallerAllowedParentSubclass);
    }

    /**
     * 过滤规则：仅保留callerCheckPackages包下的调用方
     */
    private boolean isValidCallerStack(StackTraceElement element) {
        String callerClassName = element.getClassName();

        if (callerClassName.equals(JwpRepositoryCallInterceptor.class.getName())) {
            return false;
        }

        for (String ignorePrefix : IGNORE_CLASS_PREFIXES) {
            if (callerClassName.startsWith(ignorePrefix)) {
                return false;
            }
        }

        if (callerCheckPackages.isEmpty()) {
            return true;
        }
        return callerCheckPackages.stream().anyMatch(callerClassName::startsWith);
    }

    /**
     * 判断调用方是否为允许的父类子类
     */
    private boolean isCallerAllowedParentSubclass(StackTraceElement element) {
        try {
            Class<?> callerClass = Class.forName(element.getClassName());
            return allowedCallerParents.stream()
                    .anyMatch(parent -> parent.isAssignableFrom(callerClass));
        } catch (ClassNotFoundException e) {
            log.warn("加载调用方类失败：{}", element.getClassName(), e);
            return false;
        }
    }

    // ===================== 动态配置方法（无修改） =====================
    public void addAllowedCallerParent(Class<?> parentClass) {
        if (parentClass != null) {
            allowedCallerParents.add(parentClass);
        }
    }

    public void addRepositoryRestrictPackage(String packagePrefix) {
        if (packagePrefix != null && !packagePrefix.isEmpty()) {
            repositoryRestrictPackages.add(packagePrefix);
        }
    }

    public void addCallerCheckPackage(String packagePrefix) {
        if (packagePrefix != null && !packagePrefix.isEmpty()) {
            callerCheckPackages.add(packagePrefix);
        }
    }

    public void clearAllowedCallerParents() {
        allowedCallerParents.clear();
    }

    public void clearRepositoryRestrictPackages() {
        repositoryRestrictPackages.clear();
    }

    public void clearCallerCheckPackages() {
        callerCheckPackages.clear();
    }
}